#include "RawObjectData.h"
#define _DEBUG

////////////////////////////////////////////////////////////////////////
// VertexData
////////////////////////////////////////////////////////////////////////
VertexData::VertexData( Vector3 *p )
{
	_p = p;
}
VertexData::~VertexData( )
{
#ifdef DEBUG
	cout << "~VertexData"<<endl;
#endif
	delete _p;
}
void VertexData::addParent( TriangleData *parent )
{

	_parent.push_back( parent );
}

Vector3* VertexData::getPoint( )
{
	return _p;
}
list<TriangleData*>* VertexData::getParentList()
{
	return &_parent;
}
void VertexData::ClearParents()
{
	_parent.clear();
}

Vector3* VertexData::getNormal()
{
	return _normal;
}

void VertexData::ComputeNormal()
{
	Vector3 summNormals;
	list<TriangleData*>::iterator it = _parent.begin();

	while(it != _parent.end()) {
		if( (*it)->getNormal() != NULL )
			summNormals += (*(*it)->getNormal());
		it++;
	}
	summNormals.normalize();
	_normal = new Vector3(summNormals);

}


////////////////////////////////////////////////////////////////////////
// TexCoordData
////////////////////////////////////////////////////////////////////////
TexCoordData::TexCoordData( const float &u,const float &v )
{
	_u = new float( u );
	_v = new float( v );
}
TexCoordData::~TexCoordData( )
{
#ifdef DEBUG
	cout << "~TexCoordData"<<endl;
#endif
	delete _u;
	delete _v;
}

float* TexCoordData::getU( ) const
{
	return _u;
}

float* TexCoordData::getV( ) const
{
	return _v;
}

void TexCoordData::RepeatUV(int xRepeat, int yRepeat)
{
	*_u = *_u * xRepeat;
	*_v = *_v * yRepeat;
}

ostream & operator<< (ostream &o, const TexCoordData &tex)
{
	o << "TexCoord(" << *(tex.getU()) << "," << *(tex.getV()) << ")";
	return o;
}


////////////////////////////////////////////////////////////////////////
// EdgeData
////////////////////////////////////////////////////////////////////////

EdgeData::EdgeData( VertexData* p1,VertexData* p2, TriangleData* parent1,TriangleData* parent2 )
{
	_p1 = p1;
	_p2 = p2;
	_parent1 = parent1;
	_parent2 = parent2;

	_smooth = false;
	_normal = NULL;
	// Is Edge a Smooth Edge?
	if( parent2 != NULL )// Edge has to TriangleParents ( maybe smooth ? )
	{
		float angle = parent1->getNormal()->getAngle(*(parent2->getNormal()));
		if( angle < GeoMath::PI )// wenn winkel kleiner 45 grad dann als smooth markieren
		{
			_smooth = true;
			_normal = new Vector3((*parent1->getNormal()) + (*parent2->getNormal()));
			_normal->normalize();
		}
	}
}

EdgeData::~EdgeData()
{
#ifdef DEBUG
	cout << "~EdgeData"<<endl;
#endif
}


TriangleData* EdgeData::getParent1() const
{
	return _parent1;
}

TriangleData* EdgeData::getParent2() const
{
	return _parent2;
}

Vector3*EdgeData::getNormal() const
{
	return _normal;
}

bool EdgeData::isSmooth() const
{
	return _smooth;
}

VertexData* EdgeData::getP1()
{
	return _p1;
}

VertexData* EdgeData::getP2()
{
	return _p2;
}


////////////////////////////////////////////////////////////////////////
// TextureData
////////////////////////////////////////////////////////////////////////
TextureData::TextureData( string texID )
{
	_texID = texID;
	xRepeat = 1;
	yRepeat = 1;
}
TextureData::TextureData( string texID, int xR, int yR )
{
	_texID = texID;
	xRepeat = xR;
	yRepeat = yR;
}
TextureData::~TextureData()
{
#ifdef DEBUG
	cout << "~TextureData"<<endl;
#endif
}

string TextureData::getTexID()
{
	return _texID;
}


int TextureData::getXRepeat()
{
	return xRepeat;
}
int TextureData::getYRepeat()
{
	return yRepeat;
}

int TextureData::getIntTexID(){
	return _intTexID;
}
void TextureData::setIntTexID(unsigned int id){
	_intTexID = id;
}

////////////////////////////////////////////////////////////////////////
// MaterialData
////////////////////////////////////////////////////////////////////////

MaterialData::MaterialData( string matName, Color *ambient, Color *diffuse, Color *specular, float *transparancy, TextureData* texture )
{
	_matName = matName;
	_ambient = ambient;

	_diffuse = diffuse;

	_specular = specular;

	_transparancy = transparancy;
	_ambient->_rgba[3] = *_transparancy;
	_diffuse->_rgba[3] = *_transparancy;
	_specular->_rgba[3] = *_transparancy;
	_texture = texture;
	//if( _texture != NULL )
		//_texture->addParent(*this);
}

MaterialData::~MaterialData()
{
#ifdef DEBUG
	cout << "~MaterialData"<<endl;
#endif
	delete _ambient;
	delete _diffuse;
	delete _specular;
	delete _transparancy;
}

string MaterialData::getMatName() const
{
	return _matName;
}

Color* MaterialData::getAmbient( ) const
{
	return _ambient;
}

Color* MaterialData::getDiffuse( ) const
{
	return _diffuse;
}

Color* MaterialData::getSpecular( ) const
{
	return _specular;
}

float* MaterialData::getTransparancy( ) const
{
	return _transparancy;
}

TextureData* MaterialData::getTexture()
{
	return _texture;
}

ostream & operator<< (ostream &o, const MaterialData &mat)
{
	o << "MatName:\t\t" << mat.getMatName() <<endl
		<< "Amb<RGBA>:\t" << *mat.getAmbient()  << endl
		<< "Diff<RGBA>:\t" << *mat.getDiffuse() << endl
		<< "Spec<RGBA>:\t" << *mat.getSpecular() << endl
		<< "Trans:\t\t" << *mat.getTransparancy() << endl;
	return o;
}


////////////////////////////////////////////////////////////////////////
// TextureData
////////////////////////////////////////////////////////////////////////
TriangleData::TriangleData( 	VertexData *v1, VertexData *v2,VertexData *v3,
					TexCoordData *tc1, TexCoordData *tc2,TexCoordData *tc3,
					MaterialData *material, unsigned char flags )
{
	_v1 = v1;
	_v1->addParent(this);
	_v2 = v2;
	_v2->addParent(this);
	_v3 = v3;
	_v3->addParent(this);
	_tc1 = tc1;
	_tc2 = tc2;
	_tc3 = tc3;
	_material = material;
	_withTexture = true;
	_modeFlags = flags;
	_normal = NULL;
	_parent = NULL;
	_edgeP1 = NULL;
	_edgeP2 = NULL;
	_edgeP3 = NULL;

	Vector3 vec1 = *(_v2->getPoint()) - *(_v1->getPoint());
	Vector3 vec2 = *(_v3->getPoint()) - *(_v2->getPoint());
	Vector3 mid = *(_v1->getPoint()) + vec1 + vec2;

	_middlePoint = new Vector3( mid.getX(), mid.getY(),mid.getZ() );


	//repeat UV tex coords
	int xr = _material->getTexture()->getXRepeat();
	int yr = _material->getTexture()->getYRepeat();
	_tc1->RepeatUV(xr,yr);
	_tc2->RepeatUV(xr,yr);
	_tc3->RepeatUV(xr,yr);
}



TriangleData::TriangleData( 	VertexData *v1, VertexData *v2,VertexData *v3, 	MaterialData *material, unsigned char flags )
{
	_v1 = v1;
	_v1->addParent(this);
	_v2 = v2;
	_v2->addParent(this);
	_v3 = v3;
	_v3->addParent(this);
	_material = material;
					//cout <<*material<<endl;
	_withTexture = false;
	_modeFlags = flags;
	_normal = NULL;
	_parent = NULL;
	_tc1 = NULL;
	_tc2 = NULL;
	_tc3 = NULL;
	_edgeP1 = NULL;
	_edgeP2 = NULL;
	_edgeP3 = NULL;


	Vector3 vec1 = *(_v2->getPoint()) - *(_v1->getPoint());
	Vector3 vec2 = *(_v3->getPoint()) - *(_v2->getPoint());
	Vector3 mid = *(_v1->getPoint()) + vec1 + vec2;

	_middlePoint = new Vector3( mid.getX(), mid.getY(),mid.getZ() );

}

TriangleData::~TriangleData()
{
#ifdef DEBUG
	cout << "~TriangleData"<<endl;
#endif
	delete _middlePoint;
}

void TriangleData::setParent( MeshData* mesh )
{
	_parent = mesh;
}

MeshData* TriangleData::getParent( )
{
	return _parent;
}

Vector3* TriangleData::getMiddlePoint()
{
	return _middlePoint;
}

VertexData* TriangleData::getP1()
{
	return _v1;
}
VertexData* TriangleData::getP2()
{
	return _v2;
}
VertexData* TriangleData::getP3()
{
	return _v3;
}

Vector3* TriangleData::getP1Normal() const
{
	if( isSmooth () )
		return _v1->getNormal();
	else
		return getNormal ();
}
Vector3* TriangleData::getP2Normal() const
{
	if( isSmooth () )
		return _v2->getNormal();
	else
		return getNormal ();
}
Vector3* TriangleData::getP3Normal() const
{
	if( isSmooth () )
		return _v3->getNormal();
	else
		return getNormal ();
}

TexCoordData* TriangleData::getTexCoord1()
{
	return _tc1;
}
TexCoordData* TriangleData::getTexCoord2()
{
	return _tc2;
}
TexCoordData* TriangleData::getTexCoord3()
{
	return _tc3;
}
MaterialData* TriangleData::getMaterial()
{
	return _material;
}

bool TriangleData::isWithTexture()
{
	return _withTexture;
}

void TriangleData::BindTexture()
{
	if( _parent != NULL )
	{

		if( _parent->getParent()!= NULL && getMaterial()->getTexture() != NULL)
		{
			_parent->getParent()->getTextureLoader()->ACTIVATE( getMaterial()->getTexture()->getIntTexID() );
		}
	}
}

void TriangleData::UnbindTexture()
{
	if( _parent != NULL )
	{
		if( _parent->getParent()!= NULL && getMaterial()->getTexture() != NULL)
		{
			_parent->getParent()->getTextureLoader()->DEACTIVATE( );
		}
	}
}

void  TriangleData::ComputeNormal()
{
	Vector3 a = ( *_v1->getPoint() ) - ( *_v2->getPoint() );
	Vector3 b = ( *_v3->getPoint() ) - ( *_v2->getPoint() );
	_normal = new Vector3( b.vecProduct(a) );

	_normal->normalize();
	//cout <<"_NORMAL: "<<*_normal<<endl;
	//cout <<"_ABSOLU: "<<_normal->getAbsolute()<<endl;
}

EdgeData* TriangleData::getEdge1()
{
	return _edgeP1;
}

EdgeData* TriangleData::getEdge2()
{
	return _edgeP2;
}

EdgeData* TriangleData::getEdge3()
{
	return _edgeP3;
}

EdgeData* TriangleData::CreateEdge( VertexData* p1 ,VertexData* p2, TriangleData* parent2 )
{

	if( (p1 == _v1 && p2 == _v2) || (p1 == _v2 && p2 == _v1) )
	{
		//cout <<"=> \t\tCREATE_EDGE: 1"<<endl;
		_edgeP1 = new EdgeData(_v1, _v2, this,parent2);
		_parent->_edges.push_back( _edgeP1 );
		return _edgeP1;
	}

	if( (p1 == _v2 && p2 == _v3) || (p1 == _v3 && p2 == _v2) )
	{//cout <<"=> \t\tCREATE_EDGE: 2"<<endl;
		_edgeP2 = new EdgeData(_v2, _v3, this,parent2);
		_parent->_edges.push_back( _edgeP2 );
		return _edgeP2;
	}

	if( (p1 == _v3 && p2 == _v1) || (p1 == _v1 && p2 == _v3) )
	{//cout <<"=> \t\tCREATE_EDGE: 3"<<endl;
		_edgeP3 = new EdgeData(_v3, _v1, this,parent2);
		_parent->_edges.push_back( _edgeP3 );
		return _edgeP3;
	}
	return NULL;
}

void TriangleData::CreateEdge( EdgeData*  edge )
{

	if( (edge->getP1() == _v1 && edge->getP2() == _v2) || (edge->getP1() == _v2 && edge->getP2() == _v1) )
	{//cout <<"=> \t\tCREATE_EDGE__: 1"<<endl;
		_edgeP1 = edge;
	}

	if( (edge->getP1() == _v2 && edge->getP2() == _v3) || (edge->getP1() == _v3 && edge->getP2() == _v2) )
	{//cout <<"=> \t\tCREATE_EDGE__: 2"<<endl;
		_edgeP2 = edge;
	}

	if( (edge->getP1() == _v3 && edge->getP2() == _v1) || (edge->getP1() == _v1 && edge->getP2() == _v3) )
	{//cout <<"=> \t\tCREATE_EDGE__: 3"<<endl;
		_edgeP3 = edge;
	}
}

TriangleData* TriangleData::findNeighbour( VertexData* p1, VertexData* p2 )
{
	for( list<TriangleData*>::iterator itp1 = p1->getParentList()->begin(); itp1 != p1->getParentList()->end();itp1++)
	{
		//cout <<" # ";
		for( list<TriangleData*>::iterator itp2 = p2->getParentList()->begin(); itp2 != p2->getParentList()->end();itp2++)
		{
			//cout <<" <> ";
			//cout <<"\t\t\t\tADDR1 - ADDR2 - ADDR3: "<<(*itp1)<<" - "<<(*itp2)<<" - "<<this<<endl;
			if( ( (*itp1) == (*itp2) ) && ((*itp1) != this) )
			{
				//cout <<"RIGHT"<<endl;
				return (*itp1);
			}
		}
	}
	return NULL;
}


// berechnet die kanten dieses dreiecks
void TriangleData::ComputeEdges()
{
	//cout <<"=> \tCOMPUTE_EDGES_BEGIN"<<endl;
	if( _edgeP1 == NULL )
	{
		//cout <<"=> \t\tCOMPUTE_EDGES: edge1"<<endl;
		TriangleData* secParent = findNeighbour( _v1, _v2 );
		if( secParent != NULL )
			secParent->CreateEdge ( CreateEdge (_v1,_v2,secParent ) );
		else
			CreateEdge (_v1,_v2,secParent );
	}
	if( _edgeP2 == NULL )
	{
		//cout <<"=> \t\tCOMPUTE_EDGES: edge2"<<endl;
		TriangleData* secParent = findNeighbour( _v2, _v3 );
		if( secParent != NULL )
			secParent->CreateEdge ( CreateEdge (_v2,_v3,secParent ) );
		else
			CreateEdge (_v2,_v3,secParent );
	}
	if( _edgeP3 == NULL )
	{
		//cout <<"=> \t\tCOMPUTE_EDGES: edge3"<<endl;
		TriangleData* secParent = findNeighbour( _v3, _v1 );
		if( secParent != NULL )
			secParent->CreateEdge ( CreateEdge (_v3,_v1,secParent ) );
		else
			CreateEdge (_v3,_v1,secParent );
	}
}

Vector3*  TriangleData::getNormal() const
{
	return _normal;
}

void  TriangleData::setNormal(Vector3* no)
{
	_normal = no;
}

void TriangleData::setP1(VertexData* v)
{
	_v1 = v;
}
void TriangleData::setP2(VertexData* v)
{
	_v2 = v;
}
void TriangleData::setP3(VertexData* v)
{
	_v3 = v;
}

bool TriangleData::IsTransparency() const
{
	return ( *(_material->getTransparancy()) < 1.0 );
}




////////////////////////////////////////////////////////////////////////
// MeshData
////////////////////////////////////////////////////////////////////////



MeshData::MeshData(string name)
{
	_name = name;
	_withTexture = false;
}

MeshData::~MeshData()
{
#ifdef DEBUG
	cout << "~MeshData()"<<endl;
#endif
	for( vector<TriangleData*>::iterator it = _triangles.begin();it != _triangles.end();it++ )
		delete (*it);

	for( vector<VertexData*>::iterator it = _vertices.begin();it != _vertices.end();it++ )
		delete (*it);

	for( vector<Vector3*>::iterator it = _normals.begin();it != _normals.end();it++ )
		delete (*it);

	for( vector<TexCoordData*>::iterator it = _texCoords.begin();it != _texCoords.end();it++ )
		delete (*it);

	for( vector<EdgeData*>::iterator it = _edges.begin();it != _edges.end();it++ )
		delete (*it);
}

void MeshData::setParent( RawObjectData* obj )
{
	_parent = obj;
}

RawObjectData* MeshData::getParent( )
{
	return _parent;
}

void MeshData::addTriangle( TriangleData* tri)
{
	_triangles.push_back(tri);
	tri->setParent(this);

	if( tri->isWithTexture())
		_withTexture = true;
}

long MeshData::getPolygonCount()
{
	return _triangles.size();
};

void MeshData::RemoveRedundantVertices()
{
	/*list<VertexData*>   normalList;
	list<VertexData*>	doubleList;
	for( vector<VertexData*>::iterator it = _vertices.begin();it != _vertices.end();it++ )
	{
		cout <<"Vertex---"<<endl;
		list<VertexData*>	l;
		for( vector<VertexData*>::iterator it2 = _vertices.begin();it2 != _vertices.end();it2++ )
		{

			if(
					(*it) != (*it2) && // wenn nicht gleiche referenz und
					(*it)->getPoint()->getX() == (*it2)->getPoint()->getX() &&// wenn x gleich
					(*it)->getPoint()->getY() == (*it2)->getPoint()->getY() &&// wenn y gleich
					(*it)->getPoint()->getZ() == (*it2)->getPoint()->getZ())// wenn z gleich
			  {
			  	cout <<"\t <double>"<<endl;
			  	doubleList.push_back((*it));
			  	l.push_back((*it));
			  }
		}

		if( l.size() == 0 )
			normalList.push_back((*it));

		for( list<VertexData*>::iterator it2 = l.begin();it2 != l.end();it2++ )
		{
			cout <<"DoubleVertex: "<<endl;
			list<TriangleData*> par;
			par = (*it2)->getParentList();
				for( list<TriangleData*>::iterator it3 = par.begin();it3 != par.end();it3++ )
				{
					if( (*it3)->getP1() == (*it2) )
					{
						cout <<"\t <swap>"<<endl;
						(*it3)->setP1( (*it) );
						break;
					}
				}
		}
	}
	_vertices.clear();
	_vertices.reserve( normalList.size() );

	for( list<VertexData*>::iterator it = normalList.begin();it != doubleList.end();it++ )
	{
		cout <<"Push Normal Vertices: "<<endl;
		_vertices.push_back( (*it) );
	}

	for( list<VertexData*>::iterator it = doubleList.begin();it != doubleList.end();it++ )
	{
		cout <<"Delete Double Vertices: "<<endl;
		delete (*it);
	}*/
};

void MeshData::MergeVertices()
{
	int i = 0;
	unsigned int vertexIndex = 0;
	for( vector<VertexData*>::iterator it = _vertices.begin();it != _vertices.end();it++ )
	{
		cout <<"\t\t\t\t# "<<i<<"\t"<<*(*it)->getPoint()<<endl;

		list<VertexData*> l;
		while( vertexIndex < _vertices.size() )
		{
			VertexData* it2 = _vertices.at(vertexIndex);
			if( (*it) != (it2) )	// wenn nicht gleiche referenz und
				if(
						(*it)->getPoint()->getX() == (it2)->getPoint()->getX() &&// wenn x gleich
						(*it)->getPoint()->getY() == (it2)->getPoint()->getY() &&// wenn y gleich
						(*it)->getPoint()->getZ() == (it2)->getPoint()->getZ())// wenn z gleich
				{
					cout <<"\t\t\t\t\t == "<<*(it2)->getPoint()<<endl;
					l.push_back(it2);
					cout <<"pushed in list: index "<<vertexIndex<<endl;
				}
				else
				{
					break;
				}
			vertexIndex++;
		}
		i++;
		DivideParents( &l );
	}
};

void MeshData::DivideParents( list<VertexData*> *l )
{
	cout <<"DivideParents:ListSize: "<<l->size()<<endl;
	// liste der neuen nicht redundanten parents
	list<TriangleData*> parents;

	// alle gleiche vertexe durchlaufen
	for( list<VertexData*>::iterator vIT = l->begin();vIT != l->end();vIT++ )
	{
		//cout <<"\tDivideParents 2parentSize: "<<(*vIT)->getParentList().size()<<endl;
		// alle parents vom jeweileigen vertex sichern wenn er nicht schon vorhanden
		for( list<TriangleData*>::iterator vP = (*vIT)->getParentList()->begin();vP != (*vIT)->getParentList()->end();vP++ )
		{
			cout <<"\t\tDivideParents 3parentSize: "<<(*vIT)->getParentList()->size()<<endl;
			// pruefung ob der parent in der paretListe oben vorhanden, wenn nein dann rein damit!
			bool contain = false;
			cout <<"contain init"<<endl;
			for( list<TriangleData*>::iterator vP2 = parents.begin();vP2 != parents.end();vP2++ )
			{
				cout <<"check if contain for(...) [a] == [b] "<<*vP<<" == "<<*vP2<<endl;
				if( *vP == *vP2 )
				{
					cout <<"parent is contained!!!"<<endl;
					contain = true;
				}
			}

			if( !contain )
			{
				cout <<"contain == false => push_back"<<endl;
				parents.push_back(*vP);
			}
		}
	}

	// von jedem vertex die alten parents loeschen und neuen hinzufuegen
	for( list<VertexData*>::iterator vIT = l->begin();vIT != l->end();vIT++ )
	{
		cout <<"\tDivideParents 5"<<endl;
		// alle parents von dem vertex loeschen
		(*vIT)->ClearParents();

		// alle neuen parents hinzufuegen
		for( list<TriangleData*>::iterator vP2 = parents.begin();vP2 != parents.end();vP2++ )
			(*vIT)->addParent(*vP2);
	}
}

bool MeshData::isWithTexture()
{
	return 	_withTexture;
}





////////////////////////////////////////////////////////////////////////
// RawObjectData
////////////////////////////////////////////////////////////////////////

RawObjectData::RawObjectData()
{
	_texturesLoader = new GLTexture();
	_checkRedundants = false;
	_isAlwaysVisible = false;



	// rawDataContainer
}

RawObjectData::~RawObjectData()
{
#ifdef DEBUG
	cout <<"_~RawObjectData()_"<<endl;
#endif

	for( vector<MeshData*>::iterator it = _meshes.begin();it != _meshes.end();it++ )
		delete (*it);

	for( vector<MaterialData*>::iterator it = _materials.begin();it != _materials.end();it++ )
		delete (*it);

	for( vector<TextureData*>::iterator it = _textures.begin();it != _textures.end();it++ )
		delete (*it);

	delete _texturesLoader;
}

GLTexture* RawObjectData::getTextureLoader()
{
	return _texturesLoader;
};

string RawObjectData::getFilename()
{
	return _filename;
};

vector<MeshData*>* RawObjectData::getMeshes()
{
	return &_meshes;
};

vector<MaterialData*>* RawObjectData::getMaterials()
{
	return &_materials;
};

vector<TextureData*>* RawObjectData::getTextures()
{
	return &_textures;
};

void RawObjectData::addMesh(MeshData* mesh)
{
	_meshes.push_back(mesh);
	mesh->setParent(this);
};

long RawObjectData::getPolygonCount()
{
	long polySumm = 0;
	for( vector<MeshData*>::iterator it = _meshes.begin();it != _meshes.end();it++ )
		polySumm += (*it)->getPolygonCount();

	return polySumm;
};

bool RawObjectData::isCheckingRedundants()
{
	return _checkRedundants;
};

void RawObjectData::setCheckingRedundants(bool rr)
{
	_checkRedundants = rr;
};

void RawObjectData::setAllwaysVisible( bool value )
{
	_isAlwaysVisible = value;
}

bool RawObjectData::isAlwaysVisible()
{
	return _isAlwaysVisible;
}






